using System;
using System.Runtime.CompilerServices;
using Pinwheel.Toolkits;

namespace Pinwheel.LSerialize
{
    public unsafe partial struct LStreamBuffer : ILStreamBufferReader
    {
        public ILStreamBufferReader Reader => this;
        public int Remain => tail - index;
        
        public static LStreamBuffer* Create(byte* source, int size)
        {
            LStreamBuffer* buffer = Native.Current.AllocAndClear<LStreamBuffer>();
            buffer->head = (byte*) Native.Current.AllocAndClear(size);
            Native.Utils.Copy(buffer->head, source, size);
            buffer->length = size;
            buffer->index = 0;
            buffer->tail = size;
            return buffer;
        }
        
        public LStreamBuffer* Clone(LStreamBuffer* source)
        {
            LStreamBuffer* buffer = Native.Current.AllocAndClear<LStreamBuffer>();
            buffer->head = source->head;
            buffer->length = source->length;
            buffer->index = 0;
            buffer->tail = source->tail;
            return buffer;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool EnsureTail(int size)
        {
            // if (Free >= size) return true;
            // throw new IndexOutOfRangeException(); 
            return Remain >= size;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Reset(int size)
        {
            index = 0;
            tail = size;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public byte[] GetBuffer()
        {
            var result = new byte[tail];
            fixed (byte* p = result)
            {
                Native.Utils.Copy(p, head, tail);
            }
            return result;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int GetBuffer(byte* buffer)
        {
            if (null == buffer) return 0;
            Native.Utils.Copy(buffer, head, tail);
            return tail;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public byte PopByte()
        {
            if (EnsureTail(1))
            {
                return *(head + index++);
            }
            return default;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public byte[] PopBytes(int size)
        {
            if (EnsureTail(size))
            {
                var bytes = new byte[size];
                var span = new ReadOnlySpan<byte>(Current, size);
                span.CopyTo(bytes);
                index += size;
                return bytes; 
            }
            return null;
        }
        
        // [MethodImpl(MethodImplOptions.AggressiveInlining)]
        // public T[] PopBytes<T>(int size) where T : unmanaged
        // {
        //     EnsureTail(size);
        //     var sz = size / sizeof(T);
        //     T[] arr = new T[sz];
        //     var span = new ReadOnlySpan<T>(Current, sz);
        //     span.CopyTo(arr);
        //     index += size;
        //     return arr;
        // }
    }
}
